package mips.instructions.cop0;

import mips.instructions.Instruction;

/**
 * <code>TLBWR</code> instruction<br/>
 * Write Random TLB Entry</br>
 * @author jnmartin84@gmail.com
 */
public class TLBWR extends Instruction {

	private static final String INSTRUCTION_NAME = "TLBWR";
	private static final TLBWR INSTANCE = new TLBWR();

	private TLBWR(){}

	public static final TLBWR getInstance() {
		return INSTANCE;
	}

	/**
	 * <b>Format:</b><br/>
	 * TLBWR<br/><br/>
	 * <b>Description:</b><br/>
	 * The G bit of the TLB is written with the logical AND of the G bits in the<br/>
	 * EntryLo0 and EntryLo1 registers.<br/><br/>
	 * The TLB entry pointed at by the contents of the TLB Random register is<br/>
	 * loaded with the contents of the EntryHi and EntryLo registers.<br/><br/>
	 * <b>Operation:</b><br/>
	 * TLB[Random<sub>5...0</sub>] &larr;<br/>
	 * PageMask || (EntryHi and not PageMask) || EntryLo1 || EntryLo0
	 */
	@Override
	public final void execute(final int instruction) {
		
		// Random == 1
		// PageMask == 5
		// EntryHi == 10
		// EntryLo1 == 3
		// EntryLo0 == 2
		
		mips.COP0.TLB_Entry randomTlb = mips.COP0.JOINT_TLB[(int) (mips.COP0.CONTROL[0] & 0x0000001F)];
		
		randomTlb.entry[0] = (int) (mips.COP0.CONTROL[5] & 0x00000000FFFFFFFFL);
		randomTlb.entry[1] = (int) (mips.COP0.CONTROL[10] & 0x00000000FFFFFFFFL) & ~((int)(mips.COP0.CONTROL[5] & 0x00000000FFFFFFFFL));
		randomTlb.entry[2] = (int) (mips.COP0.CONTROL[3] & 0x00000000FFFFFFFFL);
		randomTlb.entry[3] = (int) (mips.COP0.CONTROL[2] & 0x00000000FFFFFFFFL);
		
		// MASK : bits 120-109
		randomTlb.mask = (randomTlb.entry[0] >> 13) & 0x00000FFF;
		
		// VPN2 : bits 95-77
		randomTlb.vpn2 = (randomTlb.entry[1] >> 13) & 0x0007FFFF;
		
		// G : bit 76
		randomTlb.g_flag = ((randomTlb.entry[1] >> 12) & 0x00000001) == 1;
		
		// ASID : bits 71-64
		randomTlb.asid = randomTlb.entry[1] & 0x000000FF;
		
		// PFN even : bits 56-38
		randomTlb.pfnEven = (randomTlb.entry[2] >> 6) & 0x0007FFFF;
		// C even : bits 37-35
		randomTlb.cEven = (randomTlb.entry[2] >> 3) & 0x00000007;
		// D even : bit 34
		randomTlb.dEven_flag =  ((randomTlb.entry[2] >> 2) & 0x00000001) == 1;
		// V even: bit 33
		randomTlb.vEven_flag =  ((randomTlb.entry[2] >> 1) & 0x00000001) == 1;		
		
		// PFN even : bits 56-38
		randomTlb.pfnOdd = (randomTlb.entry[3] >> 6) & 0x0007FFFF;
		// C even : bits 37-35
		randomTlb.cOdd = (randomTlb.entry[3] >> 3) & 0x00000007;
		// D even : bit 34
		randomTlb.dOdd_flag =  ((randomTlb.entry[3] >> 2) & 0x00000001) == 1;
		// V even: bit 33
		randomTlb.vOdd_flag =  ((randomTlb.entry[3] >> 1) & 0x00000001) == 1;
		
		mips.R4300i.PC = mips.R4300i.nPC;
		mips.R4300i.nPC = mips.R4300i.PC + 4;
	}
	
 	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String emit(final int instruction) {
		return	"		mips.COP0.TLB_Entry randomTlb = mips.COP0.JOINT_TLB[(int) (mips.COP0.CONTROL[0] & 0x0000001F)];\n" + 
				"		\n" + 
				"		randomTlb.entry[0] = (int) (mips.COP0.CONTROL[5] & 0x00000000FFFFFFFFL);\n" + 
				"		randomTlb.entry[1] = (int) (mips.COP0.CONTROL[10] & 0x00000000FFFFFFFFL) & ~((int)(mips.COP0.CONTROL[5] & 0x00000000FFFFFFFFL));\n" + 
				"		randomTlb.entry[2] = (int) (mips.COP0.CONTROL[3] & 0x00000000FFFFFFFFL);\n" + 
				"		randomTlb.entry[3] = (int) (mips.COP0.CONTROL[2] & 0x00000000FFFFFFFFL);\n" + 
				"		\n" + 
				"		// MASK : bits 120-109\n" + 
				"		randomTlb.mask = (randomTlb.entry[0] >> 13) & 0x00000FFF;\n" + 
				"		\n" + 
				"		// VPN2 : bits 95-77\n" + 
				"		randomTlb.vpn2 = (randomTlb.entry[1] >> 13) & 0x0007FFFF;\n" + 
				"		\n" + 
				"		// G : bit 76\n" + 
				"		randomTlb.g_flag = ((randomTlb.entry[1] >> 12) & 0x00000001) == 1;\n" + 
				"		\n" + 
				"		// ASID : bits 71-64\n" + 
				"		randomTlb.asid = randomTlb.entry[1] & 0x000000FF;\n" + 
				"		\n" + 
				"		// PFN even : bits 56-38\n" + 
				"		randomTlb.pfnEven = (randomTlb.entry[2] >> 6) & 0x0007FFFF;\n" + 
				"		// C even : bits 37-35\n" + 
				"		randomTlb.cEven = (randomTlb.entry[2] >> 3) & 0x00000007;\n" + 
				"		// D even : bit 34\n" + 
				"		randomTlb.dEven_flag =  ((randomTlb.entry[2] >> 2) & 0x00000001) == 1;\n" + 
				"		// V even: bit 33\n" + 
				"		randomTlb.vEven_flag =  ((randomTlb.entry[2] >> 1) & 0x00000001) == 1;		\n" + 
				"		\n" + 
				"		// PFN even : bits 56-38\n" + 
				"		randomTlb.pfnOdd = (randomTlb.entry[3] >> 6) & 0x0007FFFF;\n" + 
				"		// C even : bits 37-35\n" + 
				"		randomTlb.cOdd = (randomTlb.entry[3] >> 3) & 0x00000007;\n" + 
				"		// D even : bit 34\n" + 
				"		randomTlb.dOdd_flag =  ((randomTlb.entry[3] >> 2) & 0x00000001) == 1;\n" + 
				"		// V even: bit 33\n" + 
				"		randomTlb.vOdd_flag =  ((randomTlb.entry[3] >> 1) & 0x00000001) == 1;\n" + 
				"		\n" + 
				"		mips.R4300i.PC = mips.R4300i.nPC;\n" + 
				"		mips.R4300i.nPC = mips.R4300i.PC + 4;\n";
	}
	
 	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String getName(final int instruction) {
		return getName();
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String getName() {
		return INSTRUCTION_NAME;
	}
}